package hander

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"github.com/go-redsync/redsync/v4"
	"github.com/go-redsync/redsync/v4/redis/goredis/v8"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"

	"2106a-zg5/demo/order/order_srv/global"
	"2106a-zg5/demo/order/order_srv/initialization"
	"2106a-zg5/demo/order/order_srv/models"
	"2106a-zg5/demo/order/order_srv/proto"
	"2106a-zg5/demo/order/order_srv/utils"
)

type OrderService struct {
	proto.UnimplementedOrderServiceServer
}

func getOrderNum() int64 {
	t := time.Now().Unix()
	//var str string
	//for i := 1; i <= 4; i++ {
	//	str += fmt.Sprintf("%d", rand.Intn(10))
	//}
	return t

}

func (c *OrderService) OrderAdd(ctx context.Context, req *proto.OrderAddReq) (*proto.OrderAddResp, error) {
	var o models.Order
	var g models.Goods
	var u models.User
	//生成订单
	orderNo := getOrderNum()
	//首先 通过 商品id获取 商品的信息
	global.DB.Table("goods").Where("id =?", req.GoodsId).First(&g)
	//首先 通过 用户id获取 商品的信息
	global.DB.Table("user").Where("id =?", req.UserId).First(&u)
	//得到总金额
	totalAmount := g.GoodsPrice * float64(req.GoodsNum)
	o.Id = orderNo
	o.GoodsId = req.GoodsId
	o.UserId = req.UserId
	o.MerchantId = req.MerchantId
	o.GoodsNum = req.GoodsNum
	o.TotalAmount = totalAmount
	o.ShipAddress = req.ShipAddress
	o.Status = -1
	o.PayType = 1
	res := global.DB.Table("order").Create(&o)
	if res.Error != nil {
		return nil, status.Errorf(codes.Unimplemented, "Failed to create order")
	}
	//创建订单成功之后 减少 相应的库存
	g.GoodsInventory -= req.GoodsNum
	res = global.DB.Table("goods").Where("id=?", req.GoodsId).Update("goods_inventory", g.GoodsInventory)
	if res.Error != nil {
		return nil, status.Errorf(codes.Unimplemented, "Inventory deduction failed")
	}
	//支付 成功 减少用户余额
	u.UserBalance -= totalAmount
	res = global.DB.Table("user").Where("id=?", req.UserId).Update("user_balance", u.UserBalance)
	if res.Error != nil {
		return nil, status.Errorf(codes.Unimplemented, "Payment exception")
	}
	resp := modelResponse(o)
	subject := fmt.Sprintf("%d-%d", rand.Intn(10000), req.GoodsId)
	payUrl := utils.AliPay(subject, strconv.FormatInt(orderNo, 10), fmt.Sprintf("%v", totalAmount))
	initialization.Loggerssss()
	zap.S().Info("订单创建成功")
	return &proto.OrderAddResp{
		Order:      resp,
		PayAddress: payUrl,
	}, nil
}

func (c *OrderService) OrderPayStatus(ctx context.Context, req *proto.PayStatusReq) (*proto.PayStatusResp, error) {

	zap.S().Debug(fmt.Sprintf("订单号是：%d,订单状态是：%d", req.OrderNo, req.PayStatus))

	return &proto.PayStatusResp{Empty: &proto.Empty{}}, nil
}

func modelResponse(o models.Order) *proto.Order {
	return &proto.Order{
		Id:          o.Id,
		UserId:      o.UserId,
		MerchantId:  o.MerchantId,
		GoodsId:     o.GoodsId,
		GoodsNum:    o.GoodsNum,
		TotalAmount: float32(o.TotalAmount),
		Status:      o.Status,
		PayType:     int64(o.PayType),
		ShipAddress: o.ShipAddress,
	}
}

// RedisSyncLockSell 使用redis 中的 的分布式锁 来进行库存扣减
func RedisSyncLockSell(goodsId int32) {
	//dsn := fmt.Sprintf("root:wm123@tcp(laojiequ.cn:3306)/goods")

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold:             time.Second, // Slow SQL threshold
			LogLevel:                  logger.Info, // Log level
			IgnoreRecordNotFoundError: true,        // Ignore ErrRecordNotFound error for logger
			ParameterizedQueries:      true,        // Don't include params in the SQL log
			Colorful:                  true,        // Disable color
		},
	)

	db, _ := gorm.Open(mysql.Open(global.OrderConfig.Mysql), &gorm.Config{
		Logger: newLogger,
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true, // 设置为 true 表示使用单数表名
		},
	})

	//链接redis客户端
	//client := redis.NewClient(&redis.Options{
	//	Addr: "laojiequ.cn:6379", // redis ip
	//})

	pool := goredis.NewPool(global.RDB)
	rs := redsync.New(pool)
	//给他来一个唯一标识符，判断你要查询的商品有没有上锁，上锁了就等着，解锁了在继续执行
	rediskey := "goods_lock_" + strconv.Itoa(int(goodsId))

	mutex := rs.NewMutex(rediskey)

	//判断有没有上锁
	err := mutex.Lock()
	if err != nil {
		fmt.Println("上锁中")
		return
	}

	var inv models.Goods

	tx := db.Begin()

	sql := "select * from goods where id = " + strconv.Itoa(int(goodsId))

	tx.Raw(sql).Scan(&inv)

	if inv.GoodsInventory > 0 {
		sql := "update goods set goods_inventory = goods_inventory -1 where id = " + strconv.Itoa(int(goodsId))
		tx.Exec(sql)
		tx.Commit()
		mutex.Unlock()
	} else {
		tx.Rollback()
	}

}
